/**
 * Created by george on 9/30/16.
 */

var Question = mongoose.model('question');
var toolUtil = require('../common/utils/toolUtil');
var questionPraise = mongoose.model('question_praise');
var QuestionReplyThank = mongoose.model('question_replythanks');
var replyAt = mongoose.model('question_replyAt');
var replyIndex = mongoose.model('reply_index');
var async = require('async');
var Banner = mongoose.model('banner');
var geolib = require('geolib');
var Users = mongoose.model('user');
var userCommunity = mongoose.model('user_community');

var questionService = require('./questionService');
var marketService = require('./marketService');
var mutualAidService = require('./mutualAidService');

var Goods = mongoose.model('goods');
var goodsReply = mongoose.model('goods_reply');
var UserFollowups = mongoose.model('user_followups');
var SystemMessage = mongoose.model('system_messages');


/*
 --- TODO: check if we are using Mongo connection pooling
 */
/**
 * 本小区互助首页 - 微信版
 * 求助, 二手, 通知推荐, 紧急通知列表
 * 用户本人参与(发布, 回复, 收藏)的帖子以最近回复的时间倒排, 非用户参与的按发布时间倒排
 * 1. pull up 20 questions first, and find the lower & upper bounds of dateCreated; the order by dateCreated never changes
 * 2. pull up my participated stuff by dateLastModified (or dateReplied for goods) with the lower & upper bounds;
 * 3. The logic is as follows:
 *          At time t0 (e.g. = 0), we pull up 20 questions, and the t1 is determined by the earliest dateCreated.
 *      Between t0 and t1, the order of the my participated list is fixed.
 *      The next query, we'll be querying items between t1 and t2.  Now, if anything that was here during the t0->t1
 *      phase, got updated between the queries, it should have a timestamp>t0, which should be updated by SnapshotUpdate.
 *      In short, we use getWXCommunityMsgListService and getWXSnapshotUpdate in concert; at any given moment t0,
 *      we query getWXCommunityMsgListService with ts that gets lower and lower, and we query getWXSnapshotUpdate
 *      with ts that gets higher and higher.
 * @version 4.0
 * @param user
 * @param ts
 * @param direction
 * @param callback
 */

exports.getWXCommunityMsgListService = function (user, ts, direction, callback) {

    async.parallel([
        // banner
        function (callbackInner) {
            if (direction == 'top') {
                mutualAidService.getBannerListService(user, 1, callbackInner);
            } else {
                callbackInner();
            }
        },
        // topList
        function (callbackInner) {
            if (direction == 'top') {
                exports.getTopListService(user, callbackInner);
            } else {
                callbackInner();
            }
        },
        // 求助, 回复的帖子
        function (callbackInner) {
            exports.getMyQuestionListService(user, ts, direction, callbackInner);
        }
    ], function (err, results) {
        if (err)
            callback(err, null);
        else {
            var result = results[2];
            // banner
            if (results[0]) result.bannerList = results[0];
            // topList
            if (results[1]) result.topList = results[1];
            callback(null, result);
        }
    });
};

exports.getMyQuestionListService = function (user, ts, direction, callback) {
    async.waterfall([
        function (cb) {
            exports.getQuestionListService(user, ts, direction, null, cb);
        },
        function (results, cb) {
            if (results && results.list.length > 0) {
                // relying on the reverse order, t0: upper bound, t1: lower bound
                // var t0 = results.list[0].dateCreated;
                var t0 = (ts == '0') ? ts : new Date(Number(ts));
                var t1 = results.list[results.list.length - 1].dateCreated;

                console.log("getMyQuestionListService ", t0);
                console.log("getMyQuestionListService ", t1);

                // 我参与的(发的求助,回复的)
                async.parallel([
                    // 求助, 回复的帖子
                    function (callbackInner) {
                        exports.getParticipatedQuestionListService(user, t0, t1, callbackInner);
                    },
                    // 关注的
                    function (callbackInner) {
                        exports.myFollowupQuestionsService(user, t0, t1, callbackInner);
                    }
                ], function (err, result) {
                    if (err)
                        cb(err, null);
                    else {
                        // results.list = result[0];
                        if (result[0]) results.list = results.list.concat(result[0]);
                        if (result[1]) results.list = results.list.concat(result[1]);
                        console.log("getMyQuestionListService ", results.list.length);
                        console.log('participated: ', result[0].length);
                        console.log('followed: ', result[1].length);
                        cb(null, results);
                    }
                });

            } else {
                cb(null, results);
            }
        },
        function (results, cb) {
            if (results) {
                async.each(results.list, function (q, callbackInner) {
                    exports.replyHandle(q.replies, callbackInner);
                }, function (err) {
                    if (err)
                        cb(err, null);
                    else
                        cb(null, results);
                });
            } else
                cb(null, null);
        }
    ], callback);
};

exports.getTopListService = function (user, callback) {
    async.waterfall([
        function (cb) {
            Question.find({
                    community: user.community._id,
                    isValid: true,
                    topBeginDate: {$lte: new Date()},
                    topEndDate: {$gte: new Date()}
                }, '_id desc community replies status images dateCreated createdBy category topBeginDate topEndDate')
                .sort('-dateCreated')
                .populate('replies.replyTo replies.repliedBy createdBy', '_id nickName images')
                // .populate('replies.repliedBy', '_id nickName images userType authenticationStatus')
                .populate('community', '_id communityName')
                // .populate('createdBy', '_id nickName images authenticationStatus roomUnit showUnit userRole exp')
                // .limit(3)
                .exec(function (err, tops) {
                    cb(err, tops);
                });
        },
        function (results, cb) {
            exports.questionsHandle(results, user, function (err, tops) {
                var data = tops.map(function (top) {
                    var t = top.toJSON();
                    t['beginDate'] = t['topBeginDate'];
                    t['endDate'] = t['topEndDate'];
                    t.topBeginDate = undefined;
                    t.topEndDate = undefined;
                    t.isTop = true;

                    return t;
                });
                // if (tops.length > 0)
                //     data.topList[0]._doc['isTop'] = true;
                // if (tops.length > 0) {
                //     var index;
                //     data.list.forEach(function (temp, i) {
                //         if (temp._id + '' == tops[0]._id + '') {
                //             index = i;
                //         }
                //     })
                //     if (index != null) {
                //         data.list.splice(index, 1)
                //     }
                // }
                cb(err, data);
            })
        },
        function (results, cb) {
            if (results) {
                async.each(results, function (q, callbackInner) {
                    exports.replyHandle(q.replies, callbackInner);
                }, function (err) {
                    if (err)
                        cb(err, null);
                    else
                        cb(null, results);
                });
            } else
                cb(null, null);
        }
    ], callback);
};


/**
 * 个人页 - 互助
 * @param user
 * @param ts
 * @param direction
 * @param callback
 */
exports.getWXMyCommunityMsgListService = function (user, ts, direction, callback) {

    var opt = {
        isValid: true,
        community: user.community._id,
        createdBy: user._id,
        category: {$in: ['question', 'lostAndFound', 'secondhand', 'recommendation', 'alert']},
    };

    if (ts != '0') {
        opt.dateCreated = {$lt: Number(ts)}
    }

    exports.getQuestionListService(user, ts, 'down', opt, callback);

};

/**
 * 我参与(发起,回答)的问题update列表
 * TODO: add a totalRefresh msg: maybe in Redis
 * @param user
 * @param ts - dateLastModified: {$gt: new Date(Number(ts))}
 * @param callback
 */
exports.getWXSnapshotUpdateService = function (user, ts, callback) {
    console.log("wxmutualAidService::getWXSnapshotUpdateService", new Date(Number(ts)));

    var uid = mongoose.Types.ObjectId(user._id);

    async.waterfall([
        function (cb) {
            Question.aggregate([
                {
                    $match: {
                        isValid: true,
                        community: mongoose.Types.ObjectId(user.community._id),
                        dateLastModified: {$gt: new Date(Number(ts))},
                        // $and: [
                        //     {$or: [{createdBy: uid}, {'replies.repliedBy': uid}]},
                        //     {
                        //         $or: [{category: 'question'}, {category: 'lostAndFound'},
                        //             {category: 'secondhand'}, {category: 'recommendation'}, {category: 'alert'}]
                        //     }
                        // ]
                        category: {$in: ['question', 'lostAndFound', 'secondhand', 'recommendation', 'alert']},
                        $or: [{createdBy: uid}, {'replies.repliedBy': uid}]
                    }
                },
                {
                    $sort: {
                        dateLastModified: -1,
                    }
                },
                {
                    $project: {
                        replyCount: {$size: '$replies'},
                        createdBy: 1,
                        dateLastModified: 1,
                        dateCreated: 1,
                        replies: 1
                    }
                }
            ], cb);
        },
        function (results, cb) {
            var latestTs = new Date().getTime();
            Users.populate(results, {
                path: 'createdBy replies.repliedBy',
                select: '_id nickName images'
            }, function (err, populatedResults) {
                var data = {}
                data.latestTs = latestTs;
                data.list = populatedResults;
                cb(null, data);
            });
        },
        function (results, cb) {
            async.each(results.list, function (q, callbackInner) {
                exports.replyHandle(q.replies, callbackInner);
            }, function (err) {
                if (err)
                    cb(err, null);
                else
                    cb(null, results);
            });
        }
    ], callback);
};

/**
 * This will be "polled" frequently, but most of time it should return nothing
 * @param user
 * @param ts
 * @param id
 * @param callback
 */
exports.getWXReplyListenerService = function (user, ts, id, callback) {
    console.log("wxmutualAidService::getWXReplyListenerService", new Date(Number(ts)));

    exports.getQuestionReplyListener(new Date(Number(ts)), id, callback);
    // exports.getGoodsReplyListener(new Date(Number(ts)), id, callback);

};

exports.getQuestionReplyListener = function (ts, qid, callback) {

    console.log("wxmutualAidService::getQuestionReplyListener", ts);

    async.waterfall([
        function (cb) {
            Question.aggregate([
                {
                    $match: {
                        isValid: true,
                        _id: mongoose.Types.ObjectId(qid),
                        'replies.dateReplied': {$gt: ts}
                    }
                },
                {
                    $project: {
                        replyCount: {$size: '$replies'},
                        replies: 1
                    }
                },
                {
                    $unwind: '$replies'
                },
                {
                    $sort: {'replies.dateReplied': -1}
                },
                {
                    $match: {'replies.dateReplied': {$gt: ts}}
                },
                {
                    $group: {_id: {qid: '$_id'}, replies: {$push: '$replies'}}
                }
            ], cb);
        },
        function (results, cb) {
            if (results == null || results.length == 0 || results[0].replies == null || results[0].replies.length == 0) {
                cb(null, []);
                return;
            }

            Users.populate(results[0].replies, {
                path: 'repliedBy replyTo community',
                select: '_id nickName images communityName'
            }, cb);
        },
        function (results, cb) {
            exports.replyHandle(results, function (err) {
                if (err) {
                    cb(err, null);
                    return;
                }
                var res = {
                    ts: results.length > 0 ? results[0].dateReplied.getTime() : new Date().getTime(),
                    replies: results,
                    id: qid
                };
                cb(null, res);
            });

        }
    ], callback);
};

// exports.getWXSecondhandMsgList = function (user, ts, direction, callback) {
//     // 考虑把老版本发布的二手插入到question表里, 或者把新版本发布的二手插入到goods表里
//     //
// };
//
// exports.getWXMyParticipatedMsgList = function (user, ts, direction, callback) {
//     // 考虑把老版本发布的二手插入到question表里, 或者把新版本发布的二手插入到goods表里
//     //
// };


/**
 * returns my followed questions during t0 and t1 timeframe
 * @param user
 * @param t0
 * @param t1
 * @param callback
 */
exports.myFollowupQuestionsService = function (user, t0, t1, callback) {
    var matchOpt = {dateLastModified: (t0 == '0') ? {$gte: t1} : {$gte: t1, $lte: new Date(Number(t0))}};

    console.log("myFollowupQuestionsService", matchOpt);

    async.waterfall([

        function (cb) {
            UserFollowups.find({
                    category: 'question',
                    user: user._id,
                    question: {$ne: null}
                }, 'question dateCreated')
                .sort('-dateCreated')
                .populate({
                    path: 'question',
                    match: matchOpt,
                    select: '_id desc browserCount category images dateCreated dateLastModified community createdBy replies',
                    // + 'replies._id replies.images replies.repliedBy replies.dateReplied replies.replyDesc',
                    // TODO: deep popluate; don't know why it's NOT working
                    // populate: {
                    //     path: 'createdBy',
                    //     model: 'user',
                    //     select: 'nickName images'
                    // }
                })
                .exec(cb);
        },
        function (result, cb) {
            var temp = [];
            console.log(result.length);
            // console.log(result);
            for (var i = 0; i < result.length; i++) {
                if (result[i].question) {  // eliminate the ones can't find in questions; shouldn't happen though
                    result[i].question._doc.followedSince = result[i].dateCreated;
                    result[i].question._doc.shareUrl = 'http://' + backOfficeHost + QUESTION_SHARE_URL + '?v=1.0'

                    temp.push(result[i].question);
                }
            }
            Question.populate(temp,
                {
                    path: 'community createdBy replies.repliedBy replies.replyTo',
                    select: 'communityName nickName images'
                }, cb);
        },
        // function (result, cb) {
        //     async.each(result, function (question, callbackInner) {
        //             exports.replyHandle(question.replies, callbackInner);
        //         },
        //         function (err) {
        //             if (err)
        //                 cb(err, null);
        //             else
        //                 cb(null, result);
        //         });
        // }
    ], callback);
    /*
     UserFollowups.find({
     category: 'question',
     user: user._id,
     question: {$ne: null}
     }, 'question dateCreated')
     .sort('-dateCreated')
     .populate({
     path: 'question',
     match: matchOpt,
     select: '_id desc browserCount category images dateCreated dateLastModified community createdBy '
     + 'replies.repliedBy replies.images replies.repliedBy replies.dateReplied replies.replyDesc',
     // TODO: deep popluate; don't know why it's NOT working
     // populate: {
     //     path: 'createdBy',
     //     model: 'user',
     //     select: 'nickName images'
     // }
     })
     .exec(function (err, result) {
     var temp = [];
     console.log(result.length);
     // console.log(result);
     for (var i = 0; i < result.length; i++) {
     if (result[i].question) {  // eliminate the ones can't find in questions; shouldn't happen though
     result[i].question._doc.followedSince = result[i].dateCreated;
     result[i].question._doc.shareUrl = 'http://' + backOfficeHost + QUESTION_SHARE_URL + '?v=1.0'

     temp.push(result[i].question);
     }
     }
     Question.populate(temp,
     {
     path: 'community createdBy replies.repliedBy replies.replyTo',
     select: 'communityName nickName images'
     }, callback);
     })
     */
};

/**
 * internal
 * @param user
 * @param ts
 * @param direction
 * @param query
 * @param callback
 */
exports.getQuestionListService = function (user, ts, direction, query, callback) {

    var opt = query;

    if (!opt) {
        opt = {
            isValid: true,
            community: user.community._id,
            category: {$in: ['question', 'lostAndFound', 'secondhand', 'recommendation', 'alert']},
            isShow: null
            // '$or': [{category: 'question', isShow: null},
            //     {category: 'alert', isShow: null},
            //     {category: 'recommendation', isShow: null},
            //     {category: 'secondhand', isShow: null},
            //     {category: 'lostAndFound', isShow: null},
            //     // {isShow: -1, createdBy: user._id}  // isShow = -1 屏蔽掉了 should show up in user's own list
            // ]
        };

        if (direction == 'top') {
            opt.dateCreated = {$gt: new Date(Number(ts))}
        } else if (direction == 'down') {
            // opt.dateCreated = {$lt: new Date(Number(ts))}
            opt.dateCreated = {$lt: Number(ts)}
        }

        if (ts == '0') {
            delete  opt.dateCreated;
        }
    }

    console.log("getQuestionListService", opt);

    Question.find(opt, 'praiseCount _id desc categoryOrder community replies ' +
            'category status images dateCreated createdBy topicId fromFlag level')
        .populate('replies.repliedBy createdBy replies.replyTo', '_id nickName images')
        .populate('community', '_id communityName')
        // .populate('createdBy', '_id nickName images authenticationStatus roomUnit showUnit userRole exp')
        .sort({dateCreated: -1}).limit(listPageSize)
        .exec(function (err, results) {
            if (err) {
                callback(err, null)
            } else {

                exports.questionsHandle(results, user, function (err, results) {

                    if (err) {
                        callback(err, null);
                        console.log(err);
                        return;
                    }
                    var data = {};

                    //下拉刷新返回数据
                    // console.log(results);

                    if (direction == 'top' && results && results.length > 0) {

                        if (ts != '0') {
                            var count = results.length < listPageSize ? results.length : results.length + Math.ceil(Math.random() * 5);
                            data.refreshMsg = '更新了' + count + '条帖子';
                        }

                        //返回最新时间戳
                        data.latestTs = results[0].dateCreated.getTime();
                    }

                    //新帖子数量超过一页返回下一页时间戳
                    if (results.length >= listPageSize) {
                        data.nextTs = results[results.length - 1].dateCreated.getTime();
                    }

                    data.list = results.map(function (q) {
                        return q.toJSON();
                    });

                    callback(err, data);

                });
            }
        });
};

/**
 * internal
 * @param data  array of replies
 * @param callback
 */
exports.replyHandle = function (data, callback) {

    async.each(data, function (reply, callbackInner) {
        // reply = reply.toJSON();
        async.parallel([
            // function (cb) {
            //     QuestionReplyThank.findOne({
            //         reply: reply._id, user: result.createdBy
            //     }, function (err, result) {
            //         reply._doc.isThank = result != null;
            //         cb();
            //     });
            // },
            function (cb) {
                // @
                if (reply.replyDesc && reply.replyDesc.indexOf(atFlag) != -1) {

                    // console.log("replyHandle: ", reply._id + '');

                    // if (!reply._id)
                    //     console.log(reply);

                    replyAt.findOne({replyId: reply._id + ''}, function (err, at) {
                        // replyAt.findOne({createdBy: reply.repliedBy, replyId: reply._id + ''}, function (err, at) {
                        if (at != null && at.uList != null) {
                            reply['uList'] = at.uList;
                        } else 
                        console.log("uList: ", at);
                        
                        cb();
                    }).populate('uList', '_id nickName');
                } else {
                    cb()
                }
            },
            function (cb) {
                if (reply.replyTo != null && reply.quoteId != null) {
                    // 回复的回复
                    var quote = null;
                    data.forEach(function (r) {
                        // result.replies.forEach(function (r) {
                        if (reply.quoteId == r._id + '') {
                            quote = reply.replyTo.nickName + '说：' + (r.replyDesc ? r.replyDesc : '');
                        }
                    });
                    if (quote != null && quote.indexOf(atFlag) != -1) {
                        replyAt.findOne({replyId: reply.quoteId}, function (err, at) {
                            if (at != null && at.uList != null) {
                                at.uList.forEach(function (temp, i) {
                                    quote = quote.replace(new RegExp("@\\{" + i + "\\}"), '@' + temp.nickName + ' ')
                                });
                            }
                            // reply._doc['quote'] = quote;
                            reply['quote'] = quote;
                            cb();
                        }).populate('uList', '_id nickName');
                    } else {
                        // if (quote) reply._doc['quote'] = quote;
                        if (quote) reply['quote'] = quote;
                        cb();
                    }
                } else {
                    cb();
                }
            }
        ], callbackInner);
    }, callback);
    //     function (err) {
    //     var res = {
    //         ts: data.length > 0 ? data[data.length - 1].dateReplied.getTime() : new Date("2000/04/01").getTime(),
    //         replies: data,
    //         category: result.category == 'advertisement' ? 'share' : result.category,
    //         replyCount: toolUtil.transformNumber(result.replies.length)
    //     };
    //     callback(err, res)
    // })
};

/**
 * internal
 * @param results
 * @param user
 * @param callback
 */
exports.questionsHandle = function (results, user, callback) {
    async.each(results, function (question, callback) {

        /*

         var distance = geolib.getDistance(
         {latitude: user.community.location.latitude, longitude: user.community.location.longitude},
         {latitude: question.community.location.latitude, longitude: question.community.location.longitude}
         );

         if (distance > 0 && question.fromFlag != 'operate')
         question._doc.distance = distance > 1000 ? (distance / 1000).toFixed(1) + 'km' : distance + 'm';

         if (question.topicId != null) {
         question._doc.topicUrl = h5Host + '/app/topic/index.html?tid=' + question.topicId;
         }
         */
        //回复数、点赞数
        question._doc['replyCountStr'] = toolUtil.transformNumber(question.replies.length);
        question._doc.praiseCount = toolUtil.transformNumber(question.praiseCount);
        /*
         //采纳回复
         question.replies.forEach(function (reply) {
         if (reply.replyStatus == '已采纳') {
         question._doc['adoptedReply'] = reply;
         }
         });
         question.replies = undefined;

         question._doc['isTop'] = question.categoryOrder ? true : false;

         if (question.createdBy == null) {
         console.log(question._id)
         }
         //等级
         question.createdBy._doc.level = toolUtil.getUserLevel(question.createdBy.exp ? question.createdBy.exp : 0);

         //单元号
         var isOtherCommunity = user.community._id != question.community._id + '';
         if (question.createdBy.roomUnit != null && question.createdBy.roomUnit != '')
         question.createdBy._doc.roomUnit = !isNaN(question.createdBy.roomUnit) ? question.createdBy.roomUnit + '单元' : question.createdBy.roomUnit;
         if ((question.createdBy.showUnit != null && !question.createdBy.showUnit) || isOtherCommunity) {
         question.createdBy.roomUnit = undefined;
         }

         */
        //分享url
        question._doc.shareUrl = 'http://' + backOfficeHost + QUESTION_SHARE_URL + '?v=1.0';

        async.parallel([
            function (callbackInner) {              // user是否点赞
                questionPraise.findOne({
                    question: question._id, user: user._id
                }, function (err, result) {
                    question._doc.isPraise = result != null;
                    callbackInner();
                })
            },
            // function (callbackInner) {              // 回复替换@
            //     exports.replyHandle(question.replies, callbackInner);
            // },
            function (callbackInner) {              // 帖子被点赞人
                questionPraise.find({question: question._id}).sort('-dateCreated') //.limit(8)
                    .populate('user', 'nickName images userType authenticationStatus')
                    .exec(function (err, praises) {
                        if (praises != null) {
                            question._doc['praiseUsers'] = [];
                            praises.forEach(function (praise) {
                                if (praise.user != null) question._doc.praiseUsers.push(praise.user);
                            })
                        }
                        callbackInner();
                    })
            }

        ], callback)
    }, function (err) {
        callback(err, results);
    });
};

// 详情需要按会话的方式, 先显示最后的, 下拉刷新再显示以前的.
/**
 * TODO: May need pagination
 * @param user
 * @param ts
 * @param id
 * @param callback
 */
exports.getWXReplyListService = function (user, ts, id, callback) {
    Question.findOne({
            _id: id,
            isValid: true
            //$or: [{isShow: null}, {createdBy: user._id, isShow: -1}]
        }, 'createdBy replies category')
        .populate('replies.repliedBy', '_id nickName images')
        // .populate('replies.repliedBy', '_id nickName images authenticationStatus showUnit roomUnit userRole exp')
        .populate('replies.replyTo', '_id nickName')
        .populate('replies.community', 'communityName')
        .sort({'replies.dateReplied': -1})
        .exec(function (err, result) {
            if (err) logger.error(err.message);
            if (result == null) {
                callback(ErrorMessage.Q_DELETED, null);
            } else {

                var data = result.toJSON().replies;

                exports.replyHandle(data, function (err) {
                    var res = {
                        ts: data.length > 0 ? data[data.length - 1].dateReplied.getTime() : new Date("2000/04/01").getTime(),
                        replies: data,
                        category: result.category,
                        replyCount: toolUtil.transformNumber(result.replies.length)
                    };
                    callback(err, res)

                });
            }
        })
};

/**
 * 获取我参与(发布,回答)的问题列表
 * @param user
 * @param ts
 * @param callback
 */
exports.getParticipatedQuestionListService = function (user, t0, t1, callback) {

    console.log("wxmutualAidService::getParticipatedQuestionListService t0 = ", t0);
    console.log("wxmutualAidService::getParticipatedQuestionListService t1 = ", t1);

    var uid = mongoose.Types.ObjectId(user._id);

    var url = 'http://' + backOfficeHost + QUESTION_SHARE_URL + '?v=1.0';

    async.waterfall([
        function (cb) {
            Question.aggregate([
                {
                    $match: {
                        isValid: true,
                        community: mongoose.Types.ObjectId(user.community._id),
                        dateLastModified: (t0 == '0') ? {$gte: t1} : {$gte: t1, $lte: new Date(Number(t0))},
                        category: {$in: ['question', 'lostAndFound', 'secondhand', 'recommendation', 'alert']},
                        $or: [{createdBy: uid}, {'replies.repliedBy': uid}]
                    }
                },
                {
                    $project: {
                        replyCount: {$size: '$replies'},
                        createdBy: 1,
                        dateLastModified: 1,
                        dateCreated: 1,
                        community: 1,
                        desc: 1,
                        category: 1,
                        images: 1,
                        replies: 1,
                        shareUrl: {$literal: url}
                    }
                }
            ], cb);
        },
        function (results, cb) {
            Users.populate(results, {
                path: 'createdBy replies.repliedBy replies.replyTo',
                select: '_id nickName images'
            }, cb);
        },
        // function (results, cb) {
        //     var r = results.map(function (q) {
        //         return q.toJSON();
        //     })
        //     cb(null, r);
        // }
    ], callback);
};

/**
 * internal
 * @param user
 * @param ts
 * @param callback
 */
exports.getFollowGoodsListService = function (user, t0, t1, callback) {
    UserFollowups.find({
            category: 'goods',
            user: user._id,
            goods: {$ne: null},
            // dateCreated: {$lt: date}
        }, 'goods dateCreated')
        .sort('-dateCreated')
        // .limit(listPageSize)
        .populate({
            path: 'goods',
            match: {
                isValid: true,
                community: user.community._id,  // TODO: do we need this filter?
                // dealStatus: 0,              // TODO: only open deal?
                isInit: {$ne: 1}            // TODO: not in INIT stage
            },
            select: '_id title desc originalPrice presentPrice category images dateCreated releaseDate community createdBy'
            // TODO: don't know why it's NOT working
            // populate: {
            //     path: 'createdBy',
            //     model: 'user',
            //     select: 'nickName images'
            // }
        })
        .exec(function (err, result) {
            var temp = [];
            var ids = [];
            console.log(result.length);
            console.log(result);
            for (var i = 0; i < result.length; i++) {
                if (result[i].goods) {  // if doesn't exit, means populate didn't find a match, skip
                    result[i].goods._doc.followedSince = result[i].dateCreated;
                    result[i].goods._doc.detailUrl = "http://" + restHostIp + "/market/detail.html?goodsId=" + result[i].goods._id;

                    temp.push(result[i].goods);
                    ids.push(result[i].goods._id);
                }
            }
            // TODO: need work here

            async.each(temp, function (follow, callbackInner) {
                goodsReply.aggregate([
                    {
                        $match: {
                            goodsId: {$in: ids},
                            dateReplied: {$gte: t1, $lte: t0}
                        }
                    },
                    {
                        $sort: {dateReplied: -1}
                    },
                    // {
                    //     $group: {
                    //         _id: {
                    //             gid: '$goodsId'
                    //         }
                    //     }
                    // },
                    {
                        $select: 'goodsId dateReplied'
                    }

                ]);
                Goods.findOne({_id: follow.goods}, 'itemType images title presentPrice originalPrice dealStatus type createdBy dateCreated')
                    .populate('community createdBy replies.repliedBy', 'communityName nickName images')
                    .exec(function (err, goods) {
                        follow._doc.goods = goods;
                        follow._doc.detailUrl = "http://" + restHostIp + "/market/detail.html?goodsId=" + goods._id;

                        callbackInner();
                    })
            }, function (err) {
                var data = {
                    // ts: Number(ts) + 1,
                    list: result
                };
                callback(err, data);
            })

            // temp.forEach(function(err, g) {
            //     goodsReply.find({
            //         _id: {$in: goodsIds},
            //         dateReplied: {$gte: t1, $lte: t0}
            //     }
            // })
            // Goods.populate(temp,
            //     {
            //         path: 'community createdBy replies.repliedBy',
            //         select: 'communityName nickName images'
            //     }, callback);
        })
};

/**
 * 获取我回复的二手
 * @param user - matches _id, community._id
 * @param ts   - and satisfies $gt: new Date(Number(ts))
 * @param callback
 */
exports.getWXMyRepliedGoodsService = function (user, t0, t1, callback) {

    console.log("getWXMyRepliedGoodsService t0", t0);
    console.log("getWXMyRepliedGoodsService t1", t1);

    goodsReply.aggregate([
            {
                $match: {
                    isValid: true,
                    repliedBy: mongoose.Types.ObjectId(user._id),
                    community: mongoose.Types.ObjectId(user.community._id),
                    dateReplied: {$gte: t1},
                }
            },
            {
                $group: {
                    _id: '$goodsId'
                }
            }
        ])
        .exec(function (err, results) {
            if (err)
                callback(err, null);
            else {
                console.log(results.length);
                var opt = {
                    path: '_id',
                    match: (t0 == '0') ? {dateReplied: {$gte: t1}} : {dateReplied: {$gte: t1, $lte: t0}},
                    select: '_id title desc images dateCreated',
                };

                Goods.populate(results, opt, function (err, populatedResults) {
                    var goods = [];

                    // remove if can't find in Goods
                    for (var i = populatedResults.length - 1; i >= 0; i--) {
                        if (populatedResults[i]._id) {
                            goods.push(populatedResults[i]._id)
                        }
                    }

                    callback(null, goods);
                });
                // .populate('_id', 'goods_reply')

            }
        });
};

/**
 * find all my goods which replied during t0 and t1
 * @param user
 * @param t0
 * @param t1
 * @param query
 * @param callback
 */
exports.getMyGoods = function (user, t0, t1, query, callback) {

    goodsReply.aggregate([
            {
                $match: {
                    repliedBy: mongoose.Types.ObjectId(user._id),
                    community: mongoose.Types.ObjectId(user.community._id),  // TODO: necessary?
                    dateReplied: {$gte: t1, $lte: t0}
                }
            },
            {
                $group: {
                    _id: '$goodsId'
                }
            }
        ])
        .exec(function (err, results) {
            if (err)
                callback(err, null);
            else {
                console.log("getMyGoods", results.length);

                var opt = {
                    path: '_id',
                    match: {dateReplied: {$gte: t1, $lte: t0}},
                    select: '_id title desc images dateCreated',
                };

                Goods.populate(results, opt, function (err, populatedResults) {
                    var goods = [];

                    // remove if can't find in Goods
                    for (var i = populatedResults.length - 1; i >= 0; i--) {
                        if (populatedResults[i]._id) {
                            goods.push(populatedResults[i]._id)
                        }
                    }

                    callback(null, goods);
                });
                // .populate('_id', 'goods_reply')

            }
        });


    var index = Number(ts) * listPageSize;

    var field = [
        'browner',
        'collectionNum',
        'replyCount'
    ];

    var opt = {
        createdBy: user._id,
        isInit: 1,
        'images.0': {$exists: true},
        isValid: true
    };
    if (query && parseInt(query.itemType) > 0)
        opt.itemType = parseInt(query.itemType);

    Goods.find(opt, 'itemType title desc images presentPrice originalPrice dealStatus type community createdBy releaseDate location category releaseNum')
        // .skip(index).limit(listPageSize)
        .sort('-dateCreated')
        .populate('category', '_id name')
        .exec(function (err, result) {
            async.eachSeries(result, function (temp, callbackInner) {
                var key = "goods:" + temp._id + ":detail";
                redisClient.HMGET(key, field, function (err, redisValue) {
                    temp._doc.browner = redisValue[0] == null ? "0" : toolUtil.transformNumber(redisValue[0]);
                    temp._doc.collectionNum = redisValue[1] == null ? "0" : toolUtil.transformNumber(redisValue[1]);
                    temp._doc.replyCount = redisValue[2] == null ? "0" : toolUtil.transformNumber(redisValue[2]);
                    temp._doc.detailUrl = "http://" + restHostIp + "/market/detail.html?goodsId=" + temp._id;

                    var releaseDate = moment(new Date(temp.releaseDate)).format('YYYY-MM-DD');
                    var now = moment().format('YYYY-MM-DD');

                    temp._doc.isPublishNow = (releaseDate == now);

                    // if (releaseDate == now) {
                    //     temp._doc.isPublishNow = true;
                    // } else {
                    //     temp._doc.isPublishNow = false;
                    // }

                    if (temp.dealStatus == 1) {
                        temp._doc.isPublishNow = true;
                    }

                    callbackInner();
                });
            }, function (err) {
                var data = {
                    ts: Number(ts) + 1,
                    list: result
                };
                callback(err, data);
            });
        });
};
